package com.weifei.core.ioc;

import com.weifei.core.anotation.WFAutowried;
import com.weifei.core.anotation.WFController;
import com.weifei.core.anotation.WFService;
import com.weifei.core.aop.WFAdvisedSupport;
import com.weifei.core.aop.WFAopProxy;
import com.weifei.core.aop.WFCglibAopProxy;
import com.weifei.core.aop.WFJdkDynamicAopProxy;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

public class WFApplicationContext extends WFDefaultListableBeanFactory implements WFBeanFactory{

    private String[] configLocations;
    private WFBeanDefinitionReader beanDefinitionReader;
    // 单例IOC容器缓存
    private Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>();
    // 通用的IOC容器
    private Map<String, WFBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>();

    public WFApplicationContext() {}

    public WFApplicationContext(String... configLocations) throws Exception {
        this.configLocations = configLocations;
        this.refresh();
    }

    @Override
    protected void refresh() throws Exception {
        //1,定位配置文件
        this.beanDefinitionReader = new WFBeanDefinitionReader(this.configLocations);
        //2,加载配置文件，扫描相关类，并将他们封装成BeanDefinition
        List<WFBeanDefinition> beanDefinitionList = beanDefinitionReader.loadBeanDefinition();
        //3,注册，把配置信息放到容器里
        this.doRegisterBeanDefinition(beanDefinitionList);
        //4,把不是延迟加载的类提前初始化
        this.doAutowried();
    }
    private void doAutowried() throws Exception {
        for (Map.Entry<String, WFBeanDefinition> entry : super.beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            if (!entry.getValue().isLazyInit()) {
                this.getBean(beanName);
            }
        }
    }
    private void doRegisterBeanDefinition(List<WFBeanDefinition> beanDefinitions) throws Exception {
        for (WFBeanDefinition beanDefinition : beanDefinitions) {
            if (super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
                throw new Exception("The " + beanDefinition.getFactoryBeanName() + " is exists!!!");
            }
            super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
        }
    }

    /**
     * 装饰器模式
     * 1，需要保留原来的OOP关系
     * 2，需要对他进行增强
     * @param beanName
     * @return
     * @throws Exception
     */
    @Override
    public Object getBean(String beanName) throws Exception {

        WFBeanDefinition beanDefinition = super.beanDefinitionMap.get(beanName);

        //这里可以调用前置处理器

        Object instance = this.instantiateBean(beanDefinition);
        if (instance == null) {
            return null;
        }
        WFBeanWrapper beanWrapper = new WFBeanWrapper(instance);
        if (!this.factoryBeanInstanceCache.containsKey(beanName)) {
            this.factoryBeanInstanceCache.put(beanName, beanWrapper);
        }
        //这里可以调用后置处理器

        this.populateBean(beanName, instance);

        return this.factoryBeanInstanceCache
                .get(beanName)
                .getWrappedInstance();
    }

    @Override
    public Object getBean(Class<?> clazz) throws Exception {
        return getBean(clazz.getName());
    }
    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet()
                .toArray(new String[getBeanDefinitionCount()]);
    }
    public int getBeanDefinitionCount() {
        return this.beanDefinitionMap.size();
    }
    public Properties getConfig() {
        return this.beanDefinitionReader.getConfig();
    }

    /**
     * 传入一个beanDefinition,就返回一个实例Bean
     * @param beanDefinition
     * @return
     */
    private Object instantiateBean(WFBeanDefinition beanDefinition) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Object instance = null;
        String className = beanDefinition.getBeanClassName();

        //因为根据Class才能确定一个类是否有实例
        if (this.factoryBeanObjectCache.containsKey(className)) {
            instance = this.factoryBeanObjectCache.get(className);
        } else {
            Class<?> clazz = Class.forName(className);
            // 抽象类和接口特殊判断
            if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) {
               return null;
            }
            instance = clazz.newInstance();
            this.factoryBeanObjectCache.put(className, instance);
        }
        return instance;
    }

    private void populateBean(String beanName, Object instance) throws Exception {
        Class clazz = instance.getClass();
        if (!(clazz.isAnnotationPresent(WFController.class) || clazz.isAnnotationPresent(WFService.class))) {
            return;
        }
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(WFAutowried.class)) {
                continue;
            }
            WFAutowried autowried = field.getAnnotation(WFAutowried.class);
            String autowiredBeanName = autowried.value().trim();
            if ("".equals(autowiredBeanName)) {
                autowiredBeanName = field.getType().getName();
            }
            field.setAccessible(true);
            Object earlyObject = this.factoryBeanInstanceCache.get(autowiredBeanName);
            if (null == earlyObject) {
                field.set(instance, this.getBean(autowiredBeanName));
            } else {
                field.set(instance, ((WFBeanWrapper)earlyObject).getWrappedInstance());
            }
        }
    }
    private WFAopProxy createProxy(WFAdvisedSupport config) {
        Class targetClass = config.getTargetClass();
        if (targetClass.getInterfaces().length > 0) {
            return new WFJdkDynamicAopProxy(config);
        }
        return new WFCglibAopProxy();
    }
}
